using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

using EntModApp.EntServices;
using EntModApp.EntServices.Data;

namespace DemoApp.DataAccess 
{
  public partial class PersonQueryableRepository : 
    DbContextQueryableRepository<DemoAppModelDbContext, User, Person>,
    IQueryablePersonRepository
  {
    public PersonQueryableRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public PersonQueryableRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected PersonQueryableRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Users, ownContext)
    {
    }
  }

  public partial class PersonDataRepository : 
    DbContextDataRepository<DemoAppModelDbContext, User, Person, PersonLoadSettings>,
    IPersonRepository
  {
    public PersonDataRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public PersonDataRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected PersonDataRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Users, ownContext)
    {
    }

    public override IEnumerable<Person> RetrieveAll(out int total, bool retrieveTotal)
    {
      var query = new PersonLoadSettings
      {
        Paging = new PagingSettings { ComputeTotal = retrieveTotal }
      };
      return Retrieve(query, out total);
    }

    public override IEnumerable<Person> Retrieve(PersonLoadSettings query, out int total)
    {
      var dbQuery = DataSet.OfType<Person>().ApplyPersonLoadSettings(query, out total);
      return dbQuery.ToList();
    }

    public override int Count()
    {
      return DataSet.Count();
    }

    public override int Count(PersonLoadSettings query)
    {
      var dbQuery = DataSet.OfType<Person>().ApplyPersonFilterExpression(query.Filter);
      return dbQuery.Count();
    }
  }

  public static class PersonQueryableRepositoryExtensions
  {
    public static IQueryable<Person> ApplyPersonLoadSettings(this IQueryable<Person> query, PersonLoadSettings loadSettings, out int total)
    {
      if (loadSettings == null)
        loadSettings = new PersonLoadSettings();

      query = query.ApplyPersonInclusionSettings(loadSettings.Inclusion);

      query = query.ApplyPersonFilterExpression(loadSettings.Filter);

      query = query.ApplyPersonSortingSettings(loadSettings.Sorting);

      query = query.ApplyPagingSettings(loadSettings.Paging, out total);

      query = query.ApplyPersonSelectionSettings(loadSettings.Selection);

      return query;
    }

    public static IEnumerable<string> GetDirectPersonInclusionPaths(this PersonInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetDirectUserInclusionPaths())
        yield return path;


      yield break;
    }

    public static IEnumerable<string> GetPersonInclusionPaths(this PersonInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetDirectPersonInclusionPaths())
        yield return path;


      yield break;
    }

    public static IQueryable<Person> ApplyPersonInclusionSettings(this IQueryable<Person> query, PersonInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetPersonInclusionPaths())
        query = query.Include(path);

      return query;
    }

    public static IQueryable<Person> ApplyPersonFilterExpression(this IQueryable<Person> query, PersonFilterExpression filterExpression)
    {
      if (filterExpression.ByKey != null)
        query = query.Where(o => o.UserId == filterExpression.ByKey.UserId);

      var parameter = Expression.Parameter(typeof(Person), "e");
      if (filterExpression.FirstNameFilter != null)
        query = query.Where(filterExpression.FirstNameFilter.CreateExpressionBody<Person, string>(PersonPropertyNames.FirstName, parameter).ToLambda<Person>(parameter));
      if (filterExpression.LastNameFilter != null)
        query = query.Where(filterExpression.LastNameFilter.CreateExpressionBody<Person, string>(PersonPropertyNames.LastName, parameter).ToLambda<Person>(parameter));
      if (filterExpression.EmailFilter != null)
        query = query.Where(filterExpression.EmailFilter.CreateExpressionBody<Person, string>(PersonPropertyNames.Email, parameter).ToLambda<Person>(parameter));
      if (filterExpression.UserIdFilter != null)
        query = query.Where(filterExpression.UserIdFilter.CreateExpressionBody<Person, Guid>(PersonPropertyNames.UserId, parameter).ToLambda<Person>(parameter));
      if (filterExpression.UserNameFilter != null)
        query = query.Where(filterExpression.UserNameFilter.CreateExpressionBody<Person, string>(PersonPropertyNames.UserName, parameter).ToLambda<Person>(parameter));
      if (filterExpression.PasswordFilter != null)
        query = query.Where(filterExpression.PasswordFilter.CreateExpressionBody<Person, string>(PersonPropertyNames.Password, parameter).ToLambda<Person>(parameter));

      if (filterExpression.Skip.HasValue)
        query = query.Skip(filterExpression.Skip.Value);
      if (filterExpression.Take.HasValue)
        query = query.Take(filterExpression.Take.Value);

      return query;
    }

    public static IQueryable<Person> ApplyPersonSortingSettings(this IQueryable<Person> query, PersonSortingSettings sortingSettings)
    {
      return query;
    }

    public static IQueryable<Person> ApplyPersonSelectionSettings(this IQueryable<Person> query, PersonSelectionSettings selectionSettings)
    {
      return query;
    }

  }
}
